//License: Artistic License v2.0, http://opensource.org/licenses/Artistic-2.0

package main

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
)

func getStatic(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" { //illegal attempt
		http.Redirect(w, r, "/", http.StatusNotFound)
		return
	}
	writeContentGz(staticContents[r.URL.Path], false, w) //staticContents do not differentiate mobile-specific
	if conf.ServeHTTPLogging && strings.HasPrefix(r.URL.Path, "/download/") {
		go logHTTPRequest(r)
	}
}

func writeContentGz(c Content, isMobile bool, w http.ResponseWriter) {
	w.Header().Set("Content-Type", c.ContentType)
	w.Header().Set("Content-Encoding", "gzip")
	w.Header().Set("Last-Modified", c.Time)
	if isMobile {
		w.Write(c.MobileGz)
	} else {
		w.Write(c.Gz)
	}
}

/* Deprecated by getStatic
func serveDownload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" { //illegal attempt
		http.Redirect(w, r, "/", http.StatusNotFound)
		return
	}
	var file string = r.URL.Path
	if conf.ServeHTTPLogging {
		go logHTTPRequest(r)
	}
	http.ServeFile(w, r, conf.FilePath+file)
}*/

func getPage(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" { //illegal attempt
		http.Redirect(w, r, "/", http.StatusNotFound)
		return
	}
	var u UserRequest = setUserRequest(r)
	var cid string
	switch len(u.Args) {
	case 1:
		{ //http://domain
			cid = conf.DefaultLang //home page e.g. domain/en or domain/fr
		}
	default:
		{ //http://domain/blah-blah-blah
			cid = getStr(u.Args, 1)
			if cid == "" { //home page
				cid = conf.DefaultLang
			}
		}
	}
	//I can also customise page based on geolocation detection
	//and use templates that corresponds to SearchIP.  See geolocation.go and logServer.go
	//TODO: Add geolocation lookup dimension
	var c Copy = readCopy(cid)
	if c.Cid != cid { //cid not found
		cp := readSnippet(u.Lang, "Site404Copy")
		title := readSnippet(u.Lang, "Site404Title")
		var template string = "default"
		if IsMobile(r.UserAgent()) {
			template = "4Mobile"
		}
		w.Write([]byte(setPage(template, conf.DefaultLang, cp, title, "", "")))
	} else {
		writeContentGz(htmlContents[c.Cid], IsMobile(r.UserAgent()), w)
	}
	if conf.ServeHTTPLogging {
		go logHTTPRequest(r) //goroutine here does not help; the page is served already
	}
}

//search displays copies that fit the "search" criteria for blog, tag, or search
//expected URL: domain/handler/#/lang/ where handler can be search / blog / tag
//if we get domain/handler/,      display domain/handler/1/conf.DefaultLang
//if we get domain/handler/#/,    display domain/handler/#/conf.DefaultLang
//if we get domain/handler/lang/, display domain/handler/1/conf.DefaultLang
//except for tag which uses domain/handler/tagName/...
//examples
//gobl.me/search/1/en/?search=houston  or  gobl.me/search/?search=houston
//gobl.me/tag/golang/1/en  or  gobl.me/tag/golang/
//gobl.me/blog/1/en  or  gobl.me/blog/
func search(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" { //illegal attempt
		http.Redirect(w, r, "/", http.StatusNotFound)
		return
	}
	var u UserRequest = setUserRequest(r)
	var pageNum int
	var handler string = getStr(u.Args, 1)
	var urlPattern, formattedResults, seo, title string
	switch handler {
	case "search":
		{
			u.setLang(getStr(u.Args, 3)) //will default to conf.DefaultLang, if url is non-conformant
			pageNum, _ = strconv.Atoi(getStr(u.Args, 2))
			var searchTerm string = strings.TrimSpace(r.FormValue("search"))
			var results []string = searchCopies(searchTerm, u.Lang)
			searchTerm = strings.Replace(searchTerm, " ", "+", -1) //for safe reuse in pagination URL
			urlPattern = "/search/%d/%s/?search=" + searchTerm     //to navigate the result pages
			formattedResults = paginate(results, urlPattern, u.Lang, "SiteSearchList", pageNum, conf.SearchResultsPerPage)
			formattedResults = strings.Replace(readSnippet(u.Lang, "SiteSearchResultsDiv"), "<!--Results-->", formattedResults, -1)
			var template string = "default"
			if IsMobile(r.UserAgent()) {
				template = "4Mobile"
			}
			fmt.Fprintf(w, setPage(template, u.Lang, formattedResults, title, "", seo)) /**/
		}
	case "blog":
		{
			pageNum, _ = strconv.Atoi(getStr(u.Args, 2))
			if pageNum < 1 {
				pageNum = 1
			}
			u.setLang(getStr(u.Args, 3)) //will default to conf.DefaultLang, if url is non-conformant
			urlPattern = "/blog/%d/%s/"  //set independently in two places; TODO: need to rethink
			var urlPath string = fmt.Sprintf(urlPattern, pageNum, u.Lang)
			writeContentGz(htmlContents[urlPath], IsMobile(r.UserAgent()), w)
		}
	case "tag":
		{
			var tag string = getStr(u.Args, 2)
			pageNum, _ = strconv.Atoi(getStr(u.Args, 3))
			if pageNum < 1 {
				pageNum = 1
			}
			u.setLang(getStr(u.Args, 4))  //will default to conf.DefaultLang, if url is non-conformant
			urlPattern = "/tag/%s/%d/%s/" //e.g. domain/tag/tagName/2/en/
			var urlPath string = fmt.Sprintf(urlPattern, tag, pageNum, u.Lang)
			writeContentGz(htmlContents[urlPath], IsMobile(r.UserAgent()), w)
		}
	}
	if conf.ServeHTTPLogging {
		go logHTTPRequest(r)
	}
}

func paginate(slice []string, urlPattern, lang, copyFormat string, pageNum, maxPerPage int) (formattedCopies string) {
	var count int = len(slice)
	if count == 0 {
		return ""
	}
	if pageNum < 1 {
		pageNum = 1
	}
	var prevURL, nextURL, prev, next string
	if pageNum > 1 {
		prevURL = fmt.Sprintf(urlPattern, pageNum-1, lang)
		prev = readSnippet(lang, "SitePaginationPrev")
		prev = strings.Replace(prev, "{PrevPageURL}", prevURL, -1)
	}
	if (pageNum * maxPerPage) < count { //[0 .. count-1]
		nextURL = fmt.Sprintf(urlPattern, pageNum+1, lang)
		next = readSnippet(lang, "SitePaginationNext")
		next = strings.Replace(next, "{NextPageURL}", nextURL, -1)
	}
	var pageNav string = readSnippet(lang, "SitePaginationNav")
	pageNav = strings.Replace(pageNav, "{PrevPage}", prev, -1)
	pageNav = strings.Replace(pageNav, "{NextPage}", next, -1)

	var lowerBound int = ((pageNum - 1) * maxPerPage)
	var upperBound int = (pageNum * maxPerPage) - 1
	if upperBound > (count - 1) { //index is [0 .. count-1]
		upperBound = count - 1
	}
	for i := lowerBound; i <= upperBound; i++ {
		var cid string = slice[i]
		var c Copy = readCopy(cid)
		var formattedCopy string = readSnippet(lang, copyFormat)
		formattedCopy = strings.Replace(formattedCopy, "{Title}", c.Title, -1)
		formattedCopy = strings.Replace(formattedCopy, "{Cid}", c.Cid, -1)
		formattedCopy = strings.Replace(formattedCopy, "<!--Date-->", c.Date, -1)
		formattedCopy = strings.Replace(formattedCopy, "<!--Copy-->", c.Copy, -1)
		formattedCopies = formattedCopies + formattedCopy
	}
	formattedCopies = formattedCopies + pageNav
	return formattedCopies
}

//logHTTPRequest sends HTTP requests to log server set up in startHTTPLogServer.
//If the log server is not up & running yet, any log / msg sent to it will be lost.
func logHTTPRequest(r *http.Request) {
	var hlog []string
	hlog = append(hlog, strconv.FormatInt(time.Now().Unix(), 10))
	hlog = append(hlog, r.URL.String())
	hlog = append(hlog, r.RemoteAddr)
	hlog = append(hlog, r.Referer())
	hlog = append(hlog, r.Method)
	hlog = append(hlog, r.UserAgent())
	if conf.TrackLocation {
		hlog = append(hlog, GetIPLocation(r.RemoteAddr, conf.IPCityColumn, conf.IPAreaColumn, conf.IPCountryColumn))
	}
	SendMsg(hlog, logger.LogConnType, logger.LogConnAddr)
}

type UserRequest struct {
	Lang, Sid string
	Args      []string
}

func setUserRequest(r *http.Request) (u UserRequest) {
	u.Args = strings.Split(r.URL.Path, "/")
	for i := 0; i < len(u.Args); i++ {
		u.Args[i] = strings.TrimSpace(u.Args[i])
	}
	return u
}

func (u *UserRequest) setLang(s string) {
	u.Lang = strings.ToLower(strings.TrimSpace(s))
	if u.Lang == "" || !supportedLang[u.Lang] {
		u.Lang = conf.DefaultLang
	}
}

//getStr is a convenience & safety wrapper to retrieve an item from a slice
func getStr(slice []string, idx int) string {
	if len(slice) > idx {
		return slice[idx]
	}
	return ""
}

//Copyright: Slamet Hendry
